package fila;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.TreeMap;

import model.Resultado;
import model.Usuario;
import util.Gerador;

public class Caso1 {
	
	private Double lambda;
	private Double mi1;
	private Double mi2;
	private Double dp;
	
	public Caso1(Double lambda, Double mi1, Double mi2) {
		this.lambda = lambda;
		this.mi1 = mi1;
		this.mi2 = mi2;
	}
	
	public Caso1(Double lambda, Double mi1, Double mi2, Double dp) {
		this.lambda = lambda;
		this.mi1 = mi1;
		this.mi2 = mi2;
		this.dp = dp;
	}
	
	public Resultado runNormalExp(int limite) {
		List<Double> amostrasTempo1 = new ArrayList<Double>();
		List<Double> amostrasTempo2 = new ArrayList<Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = Double.POSITIVE_INFINITY;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        
        int index = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(amostrasTempo2.size() < limite) {
            if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, Gerador.getNormal(1/mi1, dp));

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
                	Usuario interrompido = fila2.get(0);
	            	interrompido.setServico(proximaSaida2 - proximaChegada);
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = Double.POSITIVE_INFINITY;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	Usuario antigo = fila2.get(0);

            	double tempo = proximaSaida2 - antigo.getChegada();
                
                amostrasTempo1.add(tempos.get(antigo.getIndice()));
                amostrasTempo2.add(tempo);
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return new Resultado(amostrasTempo1, amostrasTempo2);
	}
	
	public Resultado runDetExp(int limite) {
		List<Double> amostrasTempo1 = new ArrayList<Double>();
		List<Double> amostrasTempo2 = new ArrayList<Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

		double tempoServico = 1/this.mi1;
        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = proximaChegada + tempoServico;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        
        int index = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(amostrasTempo2.size() < limite) {
            if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, tempoServico);

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
                	Usuario interrompido = fila2.get(0);
	            	interrompido.setServico(proximaSaida2 - proximaChegada);
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = proximaChegada + tempoServico;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	Usuario antigo = fila2.get(0);

            	double tempo = proximaSaida2 - antigo.getChegada();
                
                amostrasTempo1.add(tempos.get(antigo.getIndice()));
                amostrasTempo2.add(tempo);
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return new Resultado(amostrasTempo1, amostrasTempo2);
	}
	
	public Map<Double, Double> runDetExpTransiente(int limite) {
		Map<Double, Double> data = new TreeMap<Double, Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

		double tempoServico = 1/this.mi1;
        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = proximaChegada + tempoServico;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        double log = 1.0;
        
        double total = 0.0;
        
        int index = 0;
        int i = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(i < limite) {
        	if(log < proximaChegada && log < proximaSaida1 && log < proximaSaida2) {
        		total += fila1.size() + fila2.size();
        		
        		
        		data.put(log, total/log);
        		log += 1.0;
        		
        		if(log > 5000.0) {
        			break;
        		}
        	} else if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, tempoServico);

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
                	Usuario interrompido = fila2.get(0);
	            	interrompido.setServico(proximaSaida2 - proximaChegada);
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = proximaChegada + tempoServico;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	//Usuario antigo = fila2.get(0);
            	
            	i++;

            	//double tempo = proximaSaida2 - antigo.getChegada();
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return data;
	}
	
	public Map<Double, Double> runDetExpSemContinuidadeTransiente(int limite) {
		Map<Double, Double> data = new TreeMap<Double, Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

		double tempoServico = 1/this.mi1;
        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = proximaChegada + tempoServico;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        double log = 1.0;
        
        double total = 0.0;
        
        int index = 0;
        int i = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(i < limite) {
        	if(log < proximaChegada && log < proximaSaida1 && log < proximaSaida2) {
        		total += fila1.size() + fila2.size();
        		
        		
        		data.put(log, total/log);
        		log += 1.0;
        		
        		if(log > 5000.0) {
        			break;
        		}
        	} else if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, tempoServico);

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = proximaChegada + tempoServico;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	//Usuario antigo = fila2.get(0);
            	
            	i++;

            	//double tempo = proximaSaida2 - antigo.getChegada();
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return data;
	}
	
	public Map<Double, Double> runDetExpInstavel(int limite) {
		Map<Double, Double> data = new TreeMap<Double, Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

		double tempoServico = 1/this.mi1;
        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = proximaChegada + tempoServico;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        double log = 0.0;
        
        int index = 0;
        int i = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(i < limite) {
        	if(log < proximaChegada && log < proximaSaida1 && log < proximaSaida2) {
        		data.put(log, (fila1.size() + fila2.size())*1.0);
        		log += 500.0;
        	} else if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, tempoServico);
            	
            	i++;

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
                	Usuario interrompido = fila2.get(0);
	            	interrompido.setServico(proximaSaida2 - proximaChegada);
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = proximaChegada + tempoServico;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	//Usuario antigo = fila2.get(0);
            	
            	//i++;

            	//double tempo = proximaSaida2 - antigo.getChegada();
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return data;
	}
	
	public Resultado runDetExpSemContinuidade(int limite) {
		List<Double> amostrasTempo1 = new ArrayList<Double>();
		List<Double> amostrasTempo2 = new ArrayList<Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

		double tempoServico = 1/this.mi1;
        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = proximaChegada + tempoServico;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        
        int index = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(amostrasTempo2.size() < limite) {
            if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, tempoServico);

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = proximaChegada + tempoServico;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	Usuario antigo = fila2.get(0);

            	double tempo = proximaSaida2 - antigo.getChegada();
                
                amostrasTempo1.add(tempos.get(antigo.getIndice()));
                amostrasTempo2.add(tempo);
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return new Resultado(amostrasTempo1, amostrasTempo2);
	}
	
	public Resultado runExpExp(int limite) {
		List<Double> amostrasTempo1 = new ArrayList<Double>();
		List<Double> amostrasTempo2 = new ArrayList<Double>();

		Queue<Usuario> fila1 = new LinkedList<Usuario>();
		List<Usuario> fila2 = new ArrayList<Usuario>();

        double proximaChegada = Gerador.getExponencial(this.lambda);
        double proximaSaida1 = Double.POSITIVE_INFINITY;
        double proximaSaida2 = Double.POSITIVE_INFINITY;
        
        int index = 0;
        Map<Integer, Double> tempos = new HashMap<Integer, Double>();
        
        while(amostrasTempo2.size() < limite) {
            if (proximaChegada <= proximaSaida1 && proximaChegada <= proximaSaida2) {
            	Usuario novo = new Usuario(index, proximaChegada, Gerador.getExponencial(this.mi1));

            	if (fila1.isEmpty() && fila2.isEmpty()) {
                	proximaSaida1 = proximaChegada + novo.getServico();
                } else if(fila1.isEmpty()) {
                	Usuario interrompido = fila2.get(0);
	            	interrompido.setServico(proximaSaida2 - proximaChegada);
	            	proximaSaida2 = Double.POSITIVE_INFINITY;
                	proximaSaida1 = proximaChegada + novo.getServico();
                }
 
            	fila1.add(novo);
                index++;
                proximaChegada += Gerador.getExponencial(this.lambda);
            } else if(proximaSaida1 <= proximaSaida2) {
            	Usuario antigo = fila1.poll();
            	
            	double tempo = proximaSaida1 - antigo.getChegada();
                
                tempos.put(antigo.getIndice(), tempo);
                
                Usuario novo = new Usuario(antigo.getIndice(), proximaSaida1, Gerador.getExponencial(this.mi2));
                
                proximaSaida2 = proximaSaida1 + novo.getServico();                
                fila2.add(0, novo);

                if (fila1.isEmpty()) {
                	proximaSaida1 = Double.POSITIVE_INFINITY;
                } else {
                	Double proximoServico = fila1.peek().getServico();
                	proximaSaida1 += proximoServico;
                	
                	if(!fila2.isEmpty()) {
                		proximaSaida2 += proximoServico;
                	}
                }
            } else {
            	Usuario antigo = fila2.get(0);

            	double tempo = proximaSaida2 - antigo.getChegada();
                
                amostrasTempo1.add(tempos.get(antigo.getIndice()));
                amostrasTempo2.add(tempo);
                
                fila2.remove(0);
                
                if (fila2.isEmpty())
                	proximaSaida2 = Double.POSITIVE_INFINITY;
                else {
                	proximaSaida2 += fila2.get(0).getServico();
                }
            }
        }
        
        return new Resultado(amostrasTempo1, amostrasTempo2);
	}

}
